/*
 * AIEngine a new generation network intrusion detection system.
 *
 * Copyright (C) 2013-2023  Luis Campo Giralte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 * Written by Luis Campo Giralte <luis.camp0.2009@gmail.com>
 *
 */
#include "FrequencyProtocol.h"
#include <iomanip> // setw

namespace aiengine {

FrequencyProtocol::FrequencyProtocol(const std::string &name, uint16_t protocol_layer):
	Protocol(name, protocol_layer) {}

bool FrequencyProtocol::check(const Packet &packet) {

	setHeader(packet.getPayload());
	++total_valid_packets_;
	return true;
}

void FrequencyProtocol::increaseAllocatedMemory(int value) {

	freqs_cache_->create(value);
	packet_freqs_cache_->create(value);
}

void FrequencyProtocol::decreaseAllocatedMemory(int value) {

	freqs_cache_->destroy(value);
	packet_freqs_cache_->destroy(value);
}

void FrequencyProtocol::setDynamicAllocatedMemory(bool value) {

	freqs_cache_->setDynamicAllocatedMemory(value);
	packet_freqs_cache_->setDynamicAllocatedMemory(value);
}

bool FrequencyProtocol::isDynamicAllocatedMemory() const {

	return freqs_cache_->isDynamicAllocatedMemory();
}

uint64_t FrequencyProtocol::getCurrentUseMemory() const {

	uint64_t mem = sizeof(FrequencyProtocol);

	mem += freqs_cache_->getCurrentUseMemory();
	mem += packet_freqs_cache_->getCurrentUseMemory();

	return mem;
}

uint64_t FrequencyProtocol::getAllocatedMemory() const {

        uint64_t mem = sizeof(FrequencyProtocol);

        mem += freqs_cache_->getAllocatedMemory();
        mem += packet_freqs_cache_->getAllocatedMemory();

        return mem;
}

uint64_t FrequencyProtocol::getTotalAllocatedMemory() const {

	return getAllocatedMemory();
}

void FrequencyProtocol::releaseCache() {

        if (FlowManagerPtr fm = flow_mng_.lock(); fm) {
                auto ft = fm->getFlowTable();

                std::ostringstream msg;
                msg << "Releasing " << name() << " cache";

                infoMessage(msg.str());

                uint64_t total_bytes_released_by_flows = 0;
                uint32_t release_flows = 0;

                for (auto &flow: ft) {
			bool have_release = false;
                        if (SharedPointer<Frequencies> freq = flow->frequencies; freq) { // The flow have frequencies attached
                                flow->frequencies.reset();
                                total_bytes_released_by_flows += 255; // Sizeof Frequencies class
                                freqs_cache_->release(freq);
				have_release = true;
                        }

                	if (SharedPointer<PacketFrequencies> pkt_freq = flow->packet_frequencies; pkt_freq) { // The flow have packet frequencies attached
                                flow->packet_frequencies.reset();
                                total_bytes_released_by_flows += MAX_PACKET_FREQUENCIES_VALUES; // Sizeof PacketFrequencies class aprox
                                packet_freqs_cache_->release(pkt_freq);
				have_release = true;
                        }
			if (have_release) ++release_flows;
                }
                std::string funit = "Bytes";

		data_time_ = boost::posix_time::microsec_clock::local_time();

                unitConverter(total_bytes_released_by_flows, funit);

                msg.str("");
                msg << "Release " << release_flows << " flows";
                msg << ", flow " << total_bytes_released_by_flows << " " << funit;
                infoMessage(msg.str());
        }
}

void FrequencyProtocol::releaseFlowInfo(Flow *flow) {

	if (auto finfo = flow->frequencies; finfo)
		freqs_cache_->release(finfo);

	if (auto pinfo = flow->packet_frequencies; pinfo)
		packet_freqs_cache_->release(pinfo);
}

void FrequencyProtocol::processFlow(Flow *flow) {

	CPUCycle cycles(&total_cpu_cycles_);
	++total_packets_;
	total_bytes_ += flow->packet->getLength();
	++flow->total_packets_l7;

	SharedPointer<Frequencies> freq = flow->frequencies;

	if (!freq) { // There is no Frequency object attached to the flow
		freq = freqs_cache_->acquire();
		if (freq)
			flow->frequencies = freq;
	}

	if ((freq)and(freq->packets_inspected < inspection_limit_)) {
		freq->addPayload(flow->packet->getPayload(), flow->packet->getLength());
		++ freq->packets_inspected;
                SharedPointer<PacketFrequencies> pkt_freq = flow->packet_frequencies;

                if (!pkt_freq) { // There is no Frequency object attached to the flow
                        pkt_freq = packet_freqs_cache_->acquire();
                        if (pkt_freq)
                                flow->packet_frequencies = pkt_freq;
                }
		if (pkt_freq)
                        pkt_freq->addPayload(flow->packet->getPayload(), flow->packet->getLength());
	}
}

void FrequencyProtocol::statistics(std::basic_ostream<char>& out, int level, int32_t limit) const {

	showStatisticsHeader(out, level);

	if ((level > 5)and(flow_forwarder_.lock()))
		flow_forwarder_.lock()->statistics(out);
	if (level > 3) {
		freqs_cache_->statistics(out);
		packet_freqs_cache_->statistics(out);
	}
}

void FrequencyProtocol::statistics(Json &out, int level) const {

	showStatisticsHeader(out, level);
}

CounterMap FrequencyProtocol::getCounters() const {
        CounterMap cm;

        cm.addKeyValue("packets", total_packets_);
        cm.addKeyValue("bytes", total_bytes_);

	return cm;
}

void FrequencyProtocol::resetCounters() {

	reset();
}

} // namespace aiengine
